﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace DLR01
{
    /// <summary>
    /// Demonstrate the construction of several simple dynamic methods.
    /// </summary>
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Add5: {0}", GenerateAdd5()(10));
            Console.WriteLine("Square: {0}", GenerateSquare()(12));
            Console.WriteLine("Fact: {0}", GenerateFact()(5));
        }

        private static Func<int, int> GenerateAdd5()
        {
            var param = Expression.Parameter(typeof(int), "x");
            var right = Expression.Constant(5);
            var body = Expression.Add(param, right);
            var lambda = Expression.Lambda<Func<int, int>>(body, "add5", new[] { param });
            return lambda.Compile();
        }

        public static Func<int, int> GenerateSquare()
        {
            var param = Expression.Parameter(typeof(int), "x");
            var body = Expression.Multiply(param, param);
            var lambda = Expression.Lambda<Func<int, int>>(body, "square", new[] {param });
            return lambda.Compile();
        }

        public static Func<int, int> GenerateFact()
        {
            // Creating a parameter expression:
            var value = Expression.Parameter(typeof(int), "value");

            // Creating an expression to hold a local variable:
            var result = Expression.Parameter(typeof(int), "result");

            // Creating a label to jump to from a loop:
            var label = Expression.Label(typeof(int), "label");

            // Creating a method body:
            var block = Expression.Block(
                // Adding a local variable:
                new[] { result },

                // Assigning a constant to a local variable: result = 1
                Expression.Assign(result, Expression.Constant(1)),

                // Adding a loop:
                Expression.Loop(
                    Expression.IfThenElse(
                        Expression.GreaterThan(value, Expression.Constant(1)), // if value > 1
                        Expression.MultiplyAssign(result, Expression.PostDecrementAssign(value)), // result *= (value--)
                        Expression.Break(label, result) // else goto label
                    ),
                    label) // this is the label that the loop is jumping to
            );

            return Expression.Lambda<Func<int, int>>(block, "fact", new []{ value }).Compile();
        }
    }
}